package com.unittec.zk.provider.log;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

import javax.annotation.PostConstruct;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.MessageFormatter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.scheduling.annotation.Async;

import com.alibaba.fastjson.JSON;
import com.unittec.zk.provider.ApplicationContextProvider;
import com.unittec.zk.provider.common.Md5;
import com.unittec.zk.provider.common.ServiceIdConstants;
import com.unittec.zk.provider.common.WebUtils;
import com.unittec.zk.provider.scope.GlobalScope;
import com.unittec.zk.provider.scope.ThreadLocalScope;
import com.unittec.zk.provider.service.HystrixRestTemplateService;
import com.unittec.zk.provider.utils.ExceptionUtil;
import com.unittec.zk.provider.utils.StringUtil;
import com.unittec.zk.sdk.log.request.Req102012;
import com.unittec.zk.sdk.log.request.Req102013;
import com.unittec.zk.sdk.log.request.Req102014;
import com.unittec.zk.sdk.log.request.Req102017;
import com.unittec.zk.sdk.log.response.Res102012;
import com.unittec.zk.sdk.log.response.Res102015;
import com.unittec.zk.sdk.root.common.FieldConstants;
import com.unittec.zk.sdk.root.domain.GatewayRequest;
import com.unittec.zk.sdk.root.domain.Result;
import com.unittec.zk.sdk.root.enums.LogLevelE;

/**
 * 日志采用异步的机制，由各个业务服务调用日志服务
 *
 * @author wuxinwei
 */
public class Log {

    /**
     * 添加日志级别
     */
    private static final String ADD_LOG_LEVEL_URL = "http://" + ServiceIdConstants.LOG_SERVER_102 + "/102012";

    /**
     * 记录普通业务日志的地址
     */
    private static final String BAS_LOG_CONTENT_URL = "http://" + ServiceIdConstants.LOG_SERVER_102 + "/102013";

    /**
     * 获取日志级别地址
     */
    private static final String GET_LOG_REFRESH_URL = "http://" + ServiceIdConstants.LOG_SERVER_102 + "/102015";

    /**
     * 记录系统日志的地址
     */
    private static final String SYS_LOG_CONTENT_URL = "http://" + ServiceIdConstants.LOG_SERVER_102 + "/102016";

    /**
     * 记录系统AOP日志的地址
     */
    private static final String SYS_AOP_LOG_CONTENT_URL = "http://" + ServiceIdConstants.LOG_SERVER_102 + "/102023";

    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    private static final Logger logger = LoggerFactory.getLogger(Log.class);

    private final HystrixRestTemplateService hystrixRestTemplateService;

    @Value("${spring.application.name}")
    private String serviceName;

    private volatile boolean loaded = false;

    public Log(HystrixRestTemplateService hystrixRestTemplateService) {
        this.hystrixRestTemplateService = hystrixRestTemplateService;
    }

    // 业务服务启动后，从日志服务器获取本服务的所有应用的日志级别
    private void refreshLoglevel() {
        try {
            synchronized (Log.class) {
                if (loaded) {
                    return;
                }
                Result<List<Res102015>> result = null;
                GatewayRequest<String> req102015 = new GatewayRequest<>();
                req102015.setContent(serviceName);
                ThreadLocalScope.AuthData authData = ThreadLocalScope.getAuthData();
                // 系统获取服务的所有的应用日志信息，鉴权用默认的用户和密码
                result = hystrixRestTemplateService.exchange(Md5.getMd5(FieldConstants.SYS_REQUEST_SOURCE_ID),
                    GET_LOG_REFRESH_URL, req102015, authData,
                    new ParameterizedTypeReference<Result<List<Res102015>>>() {});
                if (result.isSuccess() && result.getData() != null) {
                    List<Res102015> list = result.getData();
                    for (Res102015 res : list) {
                        GlobalScope.setLogLevel(res.getAppId(), res.getLogLevel());
                    }
                    loaded = true;
                    return;
                }
                loaded = false;
            }
        } catch (Exception e) {
            logger.error("refreshLoglevel error", e);
        }
    }

    @PostConstruct
    public void init() {
        GlobalScope.setServiceName(serviceName);
        if (ServiceIdConstants.LOG_SERVER_102.equals(serviceName)) {
            loaded = true;
        }
    }

    private GatewayRequest<Req102013> buildRequest(GatewayRequest<?> req, String log, String errmsg) {
        GatewayRequest<Req102013> req102013 = new GatewayRequest<>();
        if (req == null) {
            return req102013;
        }
        Req102013 content = new Req102013();
        content.setReqTime(LocalDateTime.now().format(DATE_FORMAT));
        content.setLogLevel(GlobalScope.getLogLevel(req.getAppId()));
        content.setServiceId(GlobalScope.getServiceName());
        content.setAppId(req.getAppId());
        content.setRequestId(req.getRequestId());
        content.setMethod(req.getMethod());
        req102013.setMethod(req.getMethod());
        // 如果是文件上传类的消息体会很大，对于超过2K的消息体，只记录轨迹，不记录日志详细内容
        if (log.length() < 2048) {
            content.setLogContent(log);
        }
        content.setErrMsg(errmsg);
        req102013.setRequestId(req.getRequestId());
        req102013.setToken(req.getToken());
        req102013.setAppId(req.getAppId());
        req102013.setContent(content);
        return req102013;
    }

    public String buildErrmsg(Throwable throwable) {
        return ExceptionUtil.getExceptionMessage(throwable);
    }

    @Async("taskExecutor")
    public void log(ThreadLocalScope.AuthData authData, GatewayRequest<Req102013> req102013, String errmsg) {
        try {
            hystrixRestTemplateService.exchange(BAS_LOG_CONTENT_URL, req102013, authData,
                new ParameterizedTypeReference<Result<String>>() {});
        } catch (Exception e) {
            // 捕获到异常后，在本地记录日志，确保日志信息不会丢失
            logger.error("log content=【{}】 errmsg=【{}】", JSON.toJSONString(req102013), errmsg, e);
        }
    }

    /**
     * 系统日志
     *
     * @param method
     * @param format
     * @param arguments
     * @return
     */
    @Async("taskExecutor")
    public void sysInfoLog(String method, String format, Object... arguments) {
        Result<String> result = null;
        GatewayRequest<Req102014> req102014 = new GatewayRequest<>();
        Req102014 content = new Req102014();
        try {
            String log = MessageFormatter.arrayFormat(format, arguments).getMessage();
            content.setLogType(2);
            content.setReqTime(LocalDateTime.now().format(DATE_FORMAT));
            content.setLogLevel(LogLevelE.INFO);
            content.setServiceId(GlobalScope.getServiceName());
            content.setLogContent(log);
            content.setMethod(method);
            req102014.setContent(content);
            WebUtils.exchange(SYS_LOG_CONTENT_URL, req102014, new ParameterizedTypeReference<Result<String>>() {});
        } catch (RuntimeException e) {
            // 捕获到异常后，在本地记录日志，确保日志信息不会丢失
            logger.error("log content=【{}】 errmsg=【{}】", JSON.toJSONString(content), e.toString(), e);
        }
    }

    /**
     * 系统aop日志
     *
     * @param method
     * @param requestId
     * @param message
     */
    @Async("taskExecutor")
    public void sysAopMethodLog(String method, String requestId, String message) {
        Result<String> result = null;
        GatewayRequest<Req102014> req102014 = new GatewayRequest<>();
        Req102014 content = new Req102014();
        try {
            content.setRequestId(requestId);
            content.setLogType(2);
            content.setReqTime(LocalDateTime.now().format(DATE_FORMAT));
            content.setLogLevel(LogLevelE.INFO);
            content.setServiceId(GlobalScope.getServiceName());
            content.setLogContent(message);
            content.setMethod(method);
            req102014.setContent(content);
            WebUtils.exchange(SYS_AOP_LOG_CONTENT_URL, req102014, new ParameterizedTypeReference<Result<String>>() {});
        } catch (RuntimeException e) {
            // 捕获到异常后，在本地记录日志，确保日志信息不会丢失
            logger.error("log content=【{}】 errmsg=【{}】", JSON.toJSONString(content), e.toString(), e);
        }
    }

    /**
     * 系统错误日志
     *
     * @param method
     * @param log
     * @param throwable
     * @return
     */
    @Async("taskExecutor")
    public void sysErrLog(String method, String log, Throwable throwable) {
        Result<String> result = null;
        GatewayRequest<Req102014> req102014 = new GatewayRequest<>();
        Req102014 content = new Req102014();
        try {
            String errmsg = buildErrmsg(throwable);
            content.setLogType(2);
            content.setReqTime(LocalDateTime.now().format(DATE_FORMAT));
            content.setLogLevel(LogLevelE.ERROR);
            content.setServiceId(GlobalScope.getServiceName());
            content.setLogContent(log);
            content.setMethod(method);
            content.setErrMsg(errmsg);
            req102014.setContent(content);
            WebUtils.exchange(SYS_LOG_CONTENT_URL, req102014, new ParameterizedTypeReference<Result<String>>() {});
        } catch (RuntimeException e) {
            // 捕获到异常后，在本地记录日志，确保日志信息不会丢失
            logger.error("log content=【{}】 errmsg=【{}】", JSON.toJSONString(content), e.toString(), e);
        }
    }

    @Async("taskExecutor")
    public void setLogLevel(String appId, LogLevelE logLevelE) {
        try {
            GatewayRequest<Req102012> gatewayRequest = new GatewayRequest<>();
            Req102012 req102012 = new Req102012();
            req102012.setAppId(appId);
            req102012.setLogLevel(logLevelE);
            req102012.setServiceId(new String[] {serviceName});
            gatewayRequest.setContent(req102012);
            // 系统获取服务的所有的应用日志信息，鉴权用默认的用户和密码
            Result<Res102012> result = hystrixRestTemplateService.exchange(
                Md5.getMd5(FieldConstants.SYS_REQUEST_SOURCE_ID), ADD_LOG_LEVEL_URL, gatewayRequest,
                ThreadLocalScope.getAuthData(), new ParameterizedTypeReference<Result<Res102012>>() {});
        } catch (Exception e) {

        }
    }

    /**
     * 校验日志级别
     *
     * @param req
     * @param configlevel
     * @return
     */
    public boolean checkLogLevel(GatewayRequest<?> req, int configlevel) {
        if (req == null) {
            return false;
        }
        if (StringUtils.isBlank(req.getRequestId())) {
            req.setRequestId(ThreadLocalScope.getAuthData().getRequestId());
        }
        if (!loaded) {
            refreshLoglevel();
        }
        String appId = req.getAppId();
        // 如果没有应用信息，是错误的使用，需要抛异常
        if (StringUtil.isEmpty(appId)) {
            return false;
        }
        // 是空的 就 添加
        if (loaded && GlobalScope.checkLogLevelIsEmpty(appId)) {
            setLogLevel(appId, LogLevelE.DEBUG);
            loaded = false;
        }
        return GlobalScope.getLogLevel(appId).getLevel() <= configlevel;
    }

    public void debug(GatewayRequest<?> req, String format, Object... arguments) {
        if (checkLogLevel(req, LogLevelE.DEBUG.getLevel())) {
            String log = MessageFormatter.arrayFormat(format, arguments).getMessage();
            GatewayRequest<Req102013> req102013 = buildRequest(req, log, "");

            ThreadLocalScope.AuthData authData = ThreadLocalScope.getAuthData();
            log(authData, req102013, "");
        }
    }

    public void debug(GatewayRequest<?> req, String log, Throwable throwable) {
        if (checkLogLevel(req, LogLevelE.DEBUG.getLevel())) {
            String errmsg = buildErrmsg(throwable);
            GatewayRequest<Req102013> req102013 = buildRequest(req, log, errmsg);

            ThreadLocalScope.AuthData authData = ThreadLocalScope.getAuthData();
            log(authData, req102013, errmsg);
        }
    }

    public void info(GatewayRequest<?> req, String format, Object... arguments) {
        if (checkLogLevel(req, LogLevelE.INFO.getLevel())) {
            String log = MessageFormatter.arrayFormat(format, arguments).getMessage();
            GatewayRequest<Req102013> req102013 = buildRequest(req, log, "");

            ThreadLocalScope.AuthData authData = ThreadLocalScope.getAuthData();
            log(authData, req102013, "");
        }
    }

    public void info(GatewayRequest<?> req, String log, Throwable throwable) {
        if (checkLogLevel(req, LogLevelE.INFO.getLevel())) {
            String errmsg = buildErrmsg(throwable);
            GatewayRequest<Req102013> req102013 = buildRequest(req, log, errmsg);

            ThreadLocalScope.AuthData authData = ThreadLocalScope.getAuthData();
            log(authData, req102013, errmsg);
        }
    }

    public void warning(GatewayRequest<?> req, String format, Object... arguments) {
        if (checkLogLevel(req, LogLevelE.WARING.getLevel())) {
            String log = MessageFormatter.arrayFormat(format, arguments).getMessage();
            GatewayRequest<Req102013> req102013 = buildRequest(req, log, "");

            ThreadLocalScope.AuthData authData = ThreadLocalScope.getAuthData();
            log(authData, req102013, "");
        }
    }

    public void warning(GatewayRequest<?> req, String log, Throwable throwable) {
        if (checkLogLevel(req, LogLevelE.WARING.getLevel())) {
            String errmsg = buildErrmsg(throwable);
            GatewayRequest<Req102013> req102013 = buildRequest(req, log, errmsg);

            ThreadLocalScope.AuthData authData = ThreadLocalScope.getAuthData();
            log(authData, req102013, errmsg);
        }
    }

    public void error(GatewayRequest<?> req, String format, Object... arguments) {
        if (checkLogLevel(req, LogLevelE.ERROR.getLevel())) {
            String log = MessageFormatter.arrayFormat(format, arguments).getMessage();
            GatewayRequest<Req102013> req102013 = buildRequest(req, log, "");

            ThreadLocalScope.AuthData authData = ThreadLocalScope.getAuthData();
            log(authData, req102013, "");
        }
    }

    public void error(GatewayRequest<?> req, String log, Throwable throwable) {
        if (checkLogLevel(req, LogLevelE.ERROR.getLevel())) {
            String errmsg = buildErrmsg(throwable);
            GatewayRequest<Req102013> req102013 = buildRequest(req, log, errmsg);

            ThreadLocalScope.AuthData authData = ThreadLocalScope.getAuthData();
            log(authData, req102013, errmsg);
        }
    }

    /**
     * 添加操作日志
     *
     * @param reqUrl
     * @param reqParam
     * @param logTitle
     * @param logContent
     * @param operateType
     * @param operateStatus
     * @param startTime
     */
    public void addOperateLog(String reqUrl, String reqParam, String logTitle, String logContent, String operateType,
        String operateStatus, long startTime) {
        long endTime = System.currentTimeMillis();
        GatewayRequest<Req102017> gatewayRequest = new GatewayRequest<>();
        Req102017 content = new Req102017();
        try {
            content.setLogType(2);
            content.setCostTime(endTime - startTime);
            content.setOperateType(operateType);
            content.setOperateStatus(operateStatus);
            content.setRequestParam(reqParam);
            content.setLogTitle(logTitle);
            content.setLogContent(logContent);
            content.setMethod(reqUrl);
            gatewayRequest.setContent(content);
            gatewayRequest.setMethod("102017");
            AsyncAddLog asyncAddLog = ApplicationContextProvider.getApplicationContext().getBean(AsyncAddLog.class);
            asyncAddLog.asyncAddOperateLog(gatewayRequest, ThreadLocalScope.getAuthData());
        } catch (RuntimeException e) {
            // 捕获到异常后，在本地记录日志，确保日志信息不会丢失
            logger.error("log content=【{}】 errmsg=【{}】", JSON.toJSONString(gatewayRequest), e.toString(), e);
        }
    }

    /**
     * 添加登录日志
     *
     * @param reqUrl
     * @param reqParam
     * @param logTitle
     * @param logContent
     * @param operateType
     * @param operateStatus
     * @param startTime
     */
    public void addLoginLog(String reqUrl, String reqParam, String logTitle, String logContent, String operateType,
        String operateStatus, long startTime) {
        long endTime = System.currentTimeMillis();
        GatewayRequest<Req102017> gatewayRequest = new GatewayRequest<>();
        Req102017 content = new Req102017();
        try {
            content.setLogType(1);
            content.setCostTime(endTime - startTime);
            content.setOperateType(operateType);
            content.setOperateStatus(operateStatus);
            content.setRequestParam(reqParam);
            content.setLogTitle(logTitle);
            content.setLogContent(logContent);
            content.setMethod(reqUrl);
            gatewayRequest.setContent(content);
            gatewayRequest.setMethod("102017");
            AsyncAddLog asyncAddLog = ApplicationContextProvider.getApplicationContext().getBean(AsyncAddLog.class);
            asyncAddLog.asyncAddOperateLog(gatewayRequest, ThreadLocalScope.getAuthData());
        } catch (RuntimeException e) {
            // 捕获到异常后，在本地记录日志，确保日志信息不会丢失
            logger.error("log content=【{}】 errmsg=【{}】", JSON.toJSONString(content), e.toString(), e);
        }
    }
}